﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

using System.Data.Entity; // needed for lambda includes

using TheCup.ApiControllers;
using System.Diagnostics;

namespace TheCup.ApiControllers
{

    public class ListController : ApiController
    {
        TheCupContext ctx = new TheCupContext();

        ApiControllers.SeasonController c = new SeasonController();

        [HttpGet]
        public IEnumerable<BestTenRoundsListEntry> GetTenBestRounds(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            rounds.ForEach(e => e.PrepareScorecardsForRankingLists());

            List<BestTenRoundsListEntry> entries = new List<BestTenRoundsListEntry>();
            foreach (Player player in players)
            {
                var sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).OrderByDescending(st => st.StablefordPoints).Take(10);

                BestTenRoundsListEntry e = new BestTenRoundsListEntry();
                e.Player = player;
                e.AverageStableford = sc.Average(avg => Convert.ToDecimal(avg.StablefordPoints));
                e.AverageBruttoscore = sc.Average(avg => Convert.ToDecimal(avg.Score));

                e.NumberOfRounds = sc.Count();
                e.LowestStableford = sc.Min(ls => ls.StablefordPoints);
                e.HighestStableford = sc.Max(ms => ms.StablefordPoints);

                entries.Add(e);
            }

            entries = entries.OrderByDescending(res => res.NumberOfRounds).ThenByDescending(n => n.AverageStableford).ToList<BestTenRoundsListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (BestTenRoundsListEntry e in entries)
            {
                if (e.AverageStableford != prevval)
                {
                    pos++;
                    prevval = e.AverageStableford;
                }
                e.Position = pos;
            }

            // I'm pretty that this code could be moved to a LinQ function - but that is for the future to show
            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<OrderOfMeritListEntry> GetOrderOfMerit(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            rounds.ForEach(e => e.PrepareScorecardsForRankingLists());

            List<OrderOfMeritListEntry> entries = new List<OrderOfMeritListEntry>();
            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                OrderOfMeritListEntry e = new OrderOfMeritListEntry();
                e.Player = player;
                e.NumberOfWinnings = sc.Where(p => p.PositionByNettoscore == 1).Count();

                e.EarningsTotal = sc.Select(s => s.OrderOfMeritPoints).Sum();
                if (e.NumberOfWinnings > 0)
                {
                    e.EarningsAverage = sc.Where(w => w.PositionByNettoscore == 1).Select(s => s.OrderOfMeritPoints).Average();
                }
                entries.Add(e);
            }

            entries = entries.OrderByDescending(res => res.EarningsTotal).ThenBy(n => n.NumberOfWinnings).ToList<OrderOfMeritListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (OrderOfMeritListEntry e in entries)
            {
                if (e.EarningsTotal != prevval)
                {
                    pos++;
                    prevval = e.EarningsTotal;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<EclecticListEntry> GetEclectic(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            List<EclecticListEntry> entries = new List<EclecticListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                EclecticListEntry e = new EclecticListEntry();
                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.S1 = sc.Min(s => s.GetBruttoScoreVsPar(1));
                e.S2 = sc.Min(s => s.GetBruttoScoreVsPar(2));
                e.S3 = sc.Min(s => s.GetBruttoScoreVsPar(3));
                e.S4 = sc.Min(s => s.GetBruttoScoreVsPar(4));
                e.S5 = sc.Min(s => s.GetBruttoScoreVsPar(5));
                e.S6 = sc.Min(s => s.GetBruttoScoreVsPar(6));
                e.S7 = sc.Min(s => s.GetBruttoScoreVsPar(7));
                e.S8 = sc.Min(s => s.GetBruttoScoreVsPar(8));
                e.S9 = sc.Min(s => s.GetBruttoScoreVsPar(9));
                e.S10 = sc.Min(s => s.GetBruttoScoreVsPar(10));
                e.S11 = sc.Min(s => s.GetBruttoScoreVsPar(11));
                e.S12 = sc.Min(s => s.GetBruttoScoreVsPar(12));
                e.S13 = sc.Min(s => s.GetBruttoScoreVsPar(13));
                e.S14 = sc.Min(s => s.GetBruttoScoreVsPar(14));
                e.S15 = sc.Min(s => s.GetBruttoScoreVsPar(15));
                e.S16 = sc.Min(s => s.GetBruttoScoreVsPar(16));
                e.S17 = sc.Min(s => s.GetBruttoScoreVsPar(17));
                e.S18 = sc.Min(s => s.GetBruttoScoreVsPar(18));
                entries.Add(e);
            }

            entries = entries.OrderBy(res => res.Total).ToList<EclecticListEntry>();

            int pos = 0;
            decimal prevval = -1000;
            foreach (EclecticListEntry e in entries)
            {
                if (e.Total != prevval)
                {
                    pos++;
                    prevval = e.Total;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<EclecticStablefordListEntry> GetEclecticStableford(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            List<EclecticStablefordListEntry> entries = new List<EclecticStablefordListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                EclecticStablefordListEntry e = new EclecticStablefordListEntry();
                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.S1 = sc.Max(s => s.GetStablefordPoints(1));
                e.S2 = sc.Max(s => s.GetStablefordPoints(2));
                e.S3 = sc.Max(s => s.GetStablefordPoints(3));
                e.S4 = sc.Max(s => s.GetStablefordPoints(4));
                e.S5 = sc.Max(s => s.GetStablefordPoints(5));
                e.S6 = sc.Max(s => s.GetStablefordPoints(6));
                e.S7 = sc.Max(s => s.GetStablefordPoints(7));
                e.S8 = sc.Max(s => s.GetStablefordPoints(8));
                e.S9 = sc.Max(s => s.GetStablefordPoints(9));
                e.S10 = sc.Max(s => s.GetStablefordPoints(10));
                e.S11 = sc.Max(s => s.GetStablefordPoints(11));
                e.S12 = sc.Max(s => s.GetStablefordPoints(12));
                e.S13 = sc.Max(s => s.GetStablefordPoints(13));
                e.S14 = sc.Max(s => s.GetStablefordPoints(14));
                e.S15 = sc.Max(s => s.GetStablefordPoints(15));
                e.S16 = sc.Max(s => s.GetStablefordPoints(16));
                e.S17 = sc.Max(s => s.GetStablefordPoints(17));
                e.S18 = sc.Max(s => s.GetStablefordPoints(18));
                entries.Add(e);
            }

            entries = entries.OrderByDescending(res => res.Total).ToList<EclecticStablefordListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (EclecticStablefordListEntry e in entries)
            {
                if (e.Total != prevval)
                {
                    pos++;
                    prevval = e.Total;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<EclecticListEntry> GetEclecticWorst(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            List<EclecticListEntry> entries = new List<EclecticListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                EclecticListEntry e = new EclecticListEntry();
                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.S1 = sc.Max(s => s.GetBruttoScoreVsPar(1));
                e.S2 = sc.Max(s => s.GetBruttoScoreVsPar(2));
                e.S3 = sc.Max(s => s.GetBruttoScoreVsPar(3));
                e.S4 = sc.Max(s => s.GetBruttoScoreVsPar(4));
                e.S5 = sc.Max(s => s.GetBruttoScoreVsPar(5));
                e.S6 = sc.Max(s => s.GetBruttoScoreVsPar(6));
                e.S7 = sc.Max(s => s.GetBruttoScoreVsPar(7));
                e.S8 = sc.Max(s => s.GetBruttoScoreVsPar(8));
                e.S9 = sc.Max(s => s.GetBruttoScoreVsPar(9));
                e.S10 = sc.Max(s => s.GetBruttoScoreVsPar(10));
                e.S11 = sc.Max(s => s.GetBruttoScoreVsPar(11));
                e.S12 = sc.Max(s => s.GetBruttoScoreVsPar(12));
                e.S13 = sc.Max(s => s.GetBruttoScoreVsPar(13));
                e.S14 = sc.Max(s => s.GetBruttoScoreVsPar(14));
                e.S15 = sc.Max(s => s.GetBruttoScoreVsPar(15));
                e.S16 = sc.Max(s => s.GetBruttoScoreVsPar(16));
                e.S17 = sc.Max(s => s.GetBruttoScoreVsPar(17));
                e.S18 = sc.Max(s => s.GetBruttoScoreVsPar(18));
                entries.Add(e);
            }

            entries = entries.OrderByDescending(res => res.Total).ToList<EclecticListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (EclecticListEntry e in entries)
            {
                if (e.Total != prevval)
                {
                    pos++;
                    prevval = e.Total;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<EclecticListEntry> GetEclecticNetto(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            List<EclecticListEntry> entries = new List<EclecticListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                EclecticListEntry e = new EclecticListEntry();
                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.S1 = sc.Min(s => s.GetNettoScoreVsPar(1));
                e.S2 = sc.Min(s => s.GetNettoScoreVsPar(2));
                e.S3 = sc.Min(s => s.GetNettoScoreVsPar(3));
                e.S4 = sc.Min(s => s.GetNettoScoreVsPar(4));
                e.S5 = sc.Min(s => s.GetNettoScoreVsPar(5));
                e.S6 = sc.Min(s => s.GetNettoScoreVsPar(6));
                e.S7 = sc.Min(s => s.GetNettoScoreVsPar(7));
                e.S8 = sc.Min(s => s.GetNettoScoreVsPar(8));
                e.S9 = sc.Min(s => s.GetNettoScoreVsPar(9));
                e.S10 = sc.Min(s => s.GetNettoScoreVsPar(10));
                e.S11 = sc.Min(s => s.GetNettoScoreVsPar(11));
                e.S12 = sc.Min(s => s.GetNettoScoreVsPar(12));
                e.S13 = sc.Min(s => s.GetNettoScoreVsPar(13));
                e.S14 = sc.Min(s => s.GetNettoScoreVsPar(14));
                e.S15 = sc.Min(s => s.GetNettoScoreVsPar(15));
                e.S16 = sc.Min(s => s.GetNettoScoreVsPar(16));
                e.S17 = sc.Min(s => s.GetNettoScoreVsPar(17));
                e.S18 = sc.Min(s => s.GetNettoScoreVsPar(18));
                entries.Add(e);
            }

            entries = entries.OrderBy(res => res.Total).ToList<EclecticListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (EclecticListEntry e in entries)
            {
                if (e.Total != prevval)
                {
                    pos++;
                    prevval = e.Total;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<WorldRankingListEntry> GetWorldRanking(string date)
        {
            DateTime toDate = DateTime.Today;
            if (!DateTime.TryParse(date, out toDate))
            {
                toDate = DateTime.Today;
            }
            DateTime fromDate = toDate.AddYears(-2);

            List<Round> rounds = GetRounds(fromDate, toDate);
            List<Player> players = GetPlayers(rounds);
            rounds.ForEach(e => e.PrepareScorecardsForRankingLists());

            List<WorldRankingListEntry> entries = new List<WorldRankingListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                WorldRankingListEntry e = new WorldRankingListEntry();

                e.EarliestMatchDate = sc.Select(s => s.Round.DateEntry).Min();
                e.LatestMatchDate = sc.Select(s => s.Round.DateEntry).Max();

                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.Divident = e.NumberOfRounds >= 20 ? e.NumberOfRounds : 20;
                e.AverageBruttoscore = sc.Select(s => s.Score).Average().Value;
                e.AverageNettoscore = sc.Select(s => s.NettoScore).Average().Value;
                e.AverageStablefordPoints = sc.Select(s => s.StablefordPoints).Average();

                e.PointsTotal = sc.Select(s => s.WorldRankingPointsByDate(DateTime.Today)).Sum();
                e.PointsWithoutAging = sc.Select(s => s.TrophyListPoints).Sum();
                e.Points = e.PointsTotal / e.Divident;

                entries.Add(e);
            }

            entries = entries.OrderByDescending(ac => ac.Player.IsActive).ThenByDescending(res => res.Points).ToList<WorldRankingListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (WorldRankingListEntry e in entries)
            {
                if (e.Points != prevval)
                {
                    pos++;
                    prevval = e.Points;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }

        [HttpGet]
        public IEnumerable<TrophyListEntry> GetTrophy(string date)
        {
            Season season = this.GetSeason(date);
            List<Round> rounds = GetRounds(season);
            List<Player> players = GetPlayers(rounds);
            rounds.ForEach(e => e.PrepareScorecardsForRankingLists());
            List<TrophyListEntry> entries = new List<TrophyListEntry>();

            foreach (Player player in players)
            {
                List<Scorecard> sc = rounds.SelectMany(s => s.Scorecards).Where(p => p.PlayerId == player.Id).ToList<Scorecard>();

                TrophyListEntry e = new TrophyListEntry();
                e.Player = player;
                e.NumberOfRounds = sc.Count;
                e.Divident = e.NumberOfRounds >= 10 ? e.NumberOfRounds : 20;
                e.AverageBruttoscore = sc.Select(s => s.Score).Average().Value;
                e.AverageNettoscore = sc.Select(s => s.NettoScore).Average().Value;
                e.AverageStablefordPoints = sc.Select(s => s.StablefordPoints).Average();

                e.LatestMatchDate = sc.Select(s => s.Round.DateEntry).Max();

                e.PointsTotal = sc.Select(s => s.TrophyListPoints).Sum();
                e.PointsWithoutDivident = sc.Select(s => s.TrophyListPoints).Average();
                e.Points = e.PointsTotal / e.Divident;
                entries.Add(e);
            }

            entries = entries.OrderByDescending(res => res.Points).ToList<TrophyListEntry>();

            int pos = 0;
            decimal prevval = 0;
            foreach (TrophyListEntry e in entries)
            {
                if (e.Points != prevval)
                {
                    pos++;
                    prevval = e.Points;
                }
                e.Position = pos;
            }

            // get duplicate positions and mark with 'T'
            var dups = entries.GroupBy(x => x.Position).Where(grp => grp.Count() > 1).Select(group => group);
            foreach (var d in dups)
            {
                entries.Where(el => el.Position == d.Key).Select(tp => { tp.TiedPositionIndicator = "T"; return tp; }).ToList();
            }

            return entries;
        }
       
        /// <summary>
        /// TODO: Compile this query or manually sql it
        /// </summary>
        /// <param name="rounds"></param>
        /// <returns></returns>
        private static List<Player> GetPlayers(List<Round> rounds)
        {
            List<Player> players = new List<Player>();
            players = rounds.SelectMany(r => r.Scorecards.Select(pl => pl.Player)).ToList<Player>();
            players = players.Distinct(new ComparePlayers()).ToList<Player>();
            return players;
        }

        /// <summary>
        /// TODO: Compile this query or manually sql it
        /// Dette er tids synderen, 1,6 sek på en 4.8 ghz maskine, 6 sek. på produktionsmaskinen (1.8ghz opteron)
        /// </summary>
        /// <param name="season"></param>
        /// <returns></returns>
        private List<Round> GetRounds(Season season)
        {
            List<Round> rounds = new List<Round>();
            return GetRounds(season.StartDate, season.EndDate).ToList<Round>();
        }

        private List<Round> GetRounds(DateTime start, DateTime end)
        {
            return (from ev in ctx.Rounds.Include(s => s.Scorecards).Include(b => b.Scorecards.Select(p => p.Tee)).Include(b => b.Scorecards.Select(p => p.Player))
                    where (ev.DateEntry >= start && ev.DateEntry <= end) && ev.IsPrivate == false
                    select ev).ToList<Round>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private Season GetSeason(string date)
        {
            DateTime dd = string.IsNullOrEmpty(date) ? DateTime.Today : DateTime.Parse(date);

            return c.Get(dd);
        }
    }
}